<template>
  <Tooltip placement="top">
    <template #title>
      <span>{{ t('component.table.settingColumn') }}</span>
    </template>
    <Popover
      placement="bottomLeft"
      trigger="click"
      :overlayClassName="`${prefixCls}__cloumn-list`"
      :getPopupContainer="getPopupContainer"
    >
      <template #title>
        <div :class="`${prefixCls}__popover-title`">
          <Checkbox
            :indeterminate="indeterminate"
            v-model:checked="checkAll"
            @change="onCheckAllChange"
          >
            {{ t('component.table.settingColumnShow') }}
          </Checkbox>

          <Checkbox
            v-model:checked="checkIndex"
            @change="handleIndexCheckChange"
          >
            {{ t('component.table.settingIndexColumnShow') }}
          </Checkbox>

          <Checkbox
            v-model:checked="checkSelect"
            @change="handleSelectCheckChange"
            :disabled="!defaultRowSelection"
          >
            {{ t('component.table.settingSelectColumnShow') }}
          </Checkbox>

          <a-button size="small" type="link" @click="reset">
            {{ t('common.resetText') }}
          </a-button>
          <a-button size="small" type="primary" @click="saveColumnConfig">
            {{ t('common.saveText') }}
          </a-button>
        </div>
      </template>

      <template #content>
        <ScrollContainer>
          <BasicTree
            v-model:value="checkedList"
            draggable
            :tree-data="plainOptions"
            :checkable="true"
            showLine
            :selectable="false"
            :field-names="{
              key: 'dataIndex',
            }"
            :checkStrictly="true"
            @check="onCheck"
            @drop="onDrop"
          >
            <template #title="dataRef">
              <div class="tree-node">
                <div>{{ dataRef.title }}</div>
                <div>
                  <Icon
                    icon="line-md:arrow-align-left"
                    :class="[
                      `${prefixCls}__fixed-left`,
                      {
                        active: dataRef.fixed === 'left',
                        disabled: !getSelectKeys().includes(dataRef.dataIndex),
                      },
                    ]"
                    @click.stop="handleColumnFixed(dataRef, 'left')"
                  />
                  <Divider type="vertical" />
                  <Icon
                    icon="line-md:arrow-align-left"
                    :class="[
                      `${prefixCls}__fixed-right`,
                      {
                        active: dataRef.fixed === 'right',
                        disabled: !getSelectKeys().includes(dataRef.dataIndex),
                      },
                    ]"
                    @click.stop="handleColumnFixed(dataRef, 'right')"
                  />
                </div>
              </div>
            </template>
          </BasicTree>
        </ScrollContainer>
      </template>
      <SettingOutlined />
    </Popover>
  </Tooltip>
</template>
<script lang="ts">
import type { BasicColumn, ColumnChangeParam } from '../../types/table';
import {
  defineComponent,
  ref,
  reactive,
  toRefs,
  watchEffect,
  nextTick,
  unref,
  computed,
} from 'vue';
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface';
import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
import Icon from '@/components/Icon/Icon.vue';
import { ScrollContainer } from '@/components/Container';
import { useI18n } from '@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import { useDesign } from '@/hooks/web/useDesign';
// import { useSortable } from '@/hooks/web/useSortable';
import { isArray, isBoolean, isFunction } from '@/utils/is';
import { getPopupContainer as getParentContainer } from '@/utils';
import { cloneDeep, omit } from 'lodash-es';
import { list, add } from '@/api/sys/sysUserColumnsManage';
import type Sortable from 'sortablejs';
import { useMessage } from '@/hooks/web/useMessage';
import { BasicTree } from '@/components/Tree';
import { onMounted } from 'vue';
import { TreeDataItem } from 'ant-design-vue/lib/tree';
import { findNode } from '@/utils/helper/treeHelper';

interface State {
  checkAll: boolean;
  isInit?: boolean;
  checkedList: string[];
  defaultCheckList: string[];
}

interface Options {
  label: string;
  value: string;
  fixed?: boolean | 'left' | 'right';
}
interface ColumnConfig {
  columns: string;
  showId: boolean;
  showCheck: boolean;
}
export default defineComponent({
  name: 'ColumnSetting',
  components: {
    SettingOutlined,
    Popover,
    Tooltip,
    Checkbox,
    CheckboxGroup: Checkbox.Group,
    DragOutlined,
    ScrollContainer,
    Divider,
    Icon,
    BasicTree,
  },
  props: {
    tableKey: String,
  },
  emits: ['columns-change'],
  setup(_, { emit, attrs }) {
    const { createMessage } = useMessage();
    const { t } = useI18n();
    const table = useTableContext();
    const defaultRowSelection = omit(
      table.getRowSelection(),
      'selectedRowKeys'
    );
    let inited = false;
    let allColumns = [] as BasicColumn[];
    const cachePlainOptions = ref<Options[]>([]);
    const plainOptions = ref<Options[] | any>([]);

    const plainSortOptions = ref<Options[]>([]);

    const columnListRef = ref<ComponentRef>(null);

    const state = reactive<State>({
      checkAll: true,
      checkedList: [],
      defaultCheckList: [],
    });

    const checkIndex = ref(false);
    const checkSelect = ref(false);

    const { prefixCls } = useDesign('basic-column-setting');

    const getValues = computed(() => {
      return unref(table?.getBindValues) || {};
    });

    // watchEffect(() => {
    //   const columns = table.getColumns();
    //   setTimeout(() => {
    //     if (columns.length && !state.isInit) {
    //       init();
    //     }
    //   });
    // });
    onMounted(() => {
      init();
    });
    watchEffect(() => {
      const values = unref(getValues);
      checkIndex.value = !!values.showIndexColumn;
      checkSelect.value = !!values.rowSelection;
    });
    function getSelectKeys() {
      return isArray(state.checkedList)
        ? state.checkedList
        : state.checkedList.checked;
    }
    function getColumns() {
      const ret: Options[] = [];
      table
        .getColumns({ ignoreIndex: true, ignoreAction: true })
        .forEach((item) => {
          ret.push({
            label: (item.title as string) || (item.customTitle as string),
            value: (item.dataIndex || item.title) as string,
            ...item,
          });
        });
      return ret;
    }
    /**
     * 上下级schema压扁成一级
     * @param nSchemas 源
     * @param oSchemas 目标
     */
    function getChildren(nSchemas, oSchemas) {
      oSchemas.forEach((os) => {
        nSchemas.push(cloneDeep(os));
        if (os.children) getChildren(nSchemas, os.children);
      });
    }
    function setColumnsaVisible(cols) {
      cols.forEach((os) => {
        if (getSelectKeys().indexOf(os.dataIndex) > -1) {
          os.ifShow = true;
        } else {
          os.ifShow = false;
        }
        if (os.children && os.children.length) setColumnsaVisible(os.children);
      });
    }
    function onDrop(info) {
      const dropKey = info.node.eventKey;
      const dragKey = info.dragNode.eventKey;
      const dropPos = info.node.pos.split('-');
      const dropPosition =
        info.dropPosition - Number(dropPos[dropPos.length - 1]);
      // 层级
      const data = [...plainOptions.value];
      // 找到拖动的数组对象
      let dragObj: TreeDataItem = {};
      findInWhole(
        data,
        'dataIndex',
        dragKey,
        (item: TreeDataItem, index: number, arr: TreeDataItem[]) => {
          arr.splice(index, 1);
          dragObj = item;
        }
      );

      let ar: TreeDataItem[] = [];
      let i = 0;
      let Id = '';
      // 找到拖动的位置
      findInWhole(
        data,
        'dataIndex',
        dropKey,
        (_: TreeDataItem, index: number, arr: TreeDataItem[], parentId) => {
          ar = arr;
          i = index;
          Id = parentId;
        }
      );

      if (!info.dropToGap) {
        // Drop on the content
        findInWhole(data, 'dataIndex', dropKey, (item: TreeDataItem) => {
          item.children = item.children || [];
          // where to insert 添加到尾部
          item.children.push(dragObj);
        });
      } else {
        if (dropPosition === -1) {
          ar.splice(i, 0, dragObj);
        } else {
          ar.splice(i + 1, 0, dragObj);
        }
      }

      plainOptions.value = cloneDeep(data); // 赋值处理好的数据，更新树数据
      table.setColumns(plainOptions.value);
    }
    // 递归处理树数据，返回找的拖动节点，和拖动的兄弟节点数组，和拖动的父亲节点的id(可根据需求自己定义)
    function findInWhole(
      list: any[],
      key = 'name',
      path: string,
      callback: any,
      parentId?: string
    ) {
      list.forEach((item, index, arr) => {
        if (item[key] === path) {
          return callback(item, index, arr, parentId);
        }
        if (item.children) {
          return findInWhole(item.children, key, path, callback, item.id ?? '');
        }
      });
    }
    async function init() {
      function reviver(key, value) {
        try {
          if (isFunction(eval(value)) || isBoolean(eval(value))) {
            return eval(value);
          }
        } catch (error) {}
        return value;
      }
      const configs = (await list(_.tableKey)) as unknown as ColumnConfig;
      let zipColumns = [] as Recordable[];
      if (configs) {
        allColumns = JSON.parse(configs.columns, reviver);
        getChildren(zipColumns, allColumns);
        state.checkedList = zipColumns
          .filter((c) => c.ifShow)
          .map((c) => c.dataIndex);
      } else {
        allColumns = getColumns();
        getChildren(zipColumns, allColumns);
        state.checkedList = zipColumns.map((c) => c.dataIndex);
      }
      plainOptions.value = allColumns;
      table.setColumns(allColumns);
    }

    // checkAll change
    function onCheckAllChange(e: CheckboxChangeEvent) {
      const checkList = plainOptions.value.map((item) => item.value);
      if (e.target.checked) {
        state.checkedList = checkList;
        plainOptions.value.forEach((c) => (c.ifShow = true));
        table.setColumns(checkList);
      } else {
        // const requireCheckedColumns = cachePlainOptions.value.filter(
        //   (c: BasicColumn) => c.customRender
        // ) as BasicColumn[];
        // const requireCheckFields = requireCheckedColumns.map(
        //   (c) => c.dataIndex
        // ) as string[];
        // state.checkedList = requireCheckFields;
        // setColumns(requireCheckedColumns);
        plainOptions.value.forEach((c) => (c.ifShow = false));
        state.checkedList = [];
        table.setColumns([]);
      }
    }

    const indeterminate = computed(() => {
      const len = plainOptions.value.length;
      let checkedLen = state.checkedList.length;
      // unref(checkIndex) && checkedLen--;
      return checkedLen > 0 && checkedLen < len;
    });

    // Trigger when check/uncheck a column
    function onChange(checkedList: string[], record, a) {}
    function onCheck(keys, record) {
      const { checked } = keys;
      const cols = cloneDeep(getColumns());
      const { node, checked: isChecked } = record;
      const { children, parent } = node;
      // 有子节点
      // if (children) {
      //   let { checked } = state.checkedList as Recordable;
      //   console.log(checked, node, 'node check');
      //   // 选中 则选中所有子节点
      //   if (isChecked) {
      //     children.forEach((c) => {
      //       if (checked.indexOf(c.dataIndex) === -1) {
      //         checked.push(c.dataIndex);
      //       }
      //     });
      //   } else {
      //     children.forEach((c) => {
      //       state.checkedList.checked = state.checkedList.checked.filter(
      //         (chk) => chk !== c.dataIndex
      //       );
      //     });
      //   }
      // }
      setColumnsaVisible(plainOptions.value);
      table.setColumns(plainOptions.value);
    }

    let sortable: Sortable;
    let sortableOrder: string[] = [];
    // reset columns
    function reset() {
      state.checkedList = [...state.defaultCheckList];
      state.checkAll = true;
      plainOptions.value = unref(cachePlainOptions);
      plainSortOptions.value = unref(cachePlainOptions);
      setColumns(unref(cachePlainOptions));
      sortable.sort(sortableOrder);
    }
    async function saveColumnConfig() {
      function replacer(key, value) {
        if (isFunction(value) || isBoolean(value)) {
          return value.toString();
        }
        return value;
      }
      await add({
        columns: JSON.stringify(plainOptions.value, replacer),
        tableKey: _.tableKey,
      });
      createMessage.success('保存成功');
    }

    // Control whether the serial number column is displayed
    function handleIndexCheckChange(e: CheckboxChangeEvent) {
      table.setProps({
        showIndexColumn: e.target.checked,
      });
    }

    // Control whether the check box is displayed
    function handleSelectCheckChange(e: CheckboxChangeEvent) {
      table.setProps({
        rowSelection: e.target.checked ? defaultRowSelection : undefined,
      });
    }

    function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
      const keys = getSelectKeys();
      if (!keys.includes(item.dataIndex as string)) return;
      const node = findNode(
        plainOptions.value,
        (n) => n.dataIndex === item.dataIndex
      );
      const isFixed = item.fixed === fixed ? false : fixed;
      node.fixed = isFixed;
      table.setCacheColumnsByField?.(item.dataIndex as string, {
        fixed: isFixed,
      });
      table.setColumns(plainOptions.value);
    }

    function setColumns(columns: BasicColumn[] | string[]) {
      table.setColumns(columns);
      const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
        const visible =
          columns.findIndex(
            (c: BasicColumn | string) =>
              c === col.value ||
              (typeof c !== 'string' && c.dataIndex === col.value)
          ) !== -1;
        return { dataIndex: col.value, fixed: col.fixed, visible };
      });

      emit('columns-change', data);
    }

    function getPopupContainer() {
      return isFunction(attrs.getPopupContainer)
        ? attrs.getPopupContainer()
        : getParentContainer();
    }

    return {
      t,
      ...toRefs(state),
      indeterminate,
      onCheckAllChange,
      onChange,
      onCheck,
      plainOptions,
      reset,
      getSelectKeys,
      onDrop,
      saveColumnConfig,
      prefixCls,
      columnListRef,
      checkIndex,
      checkSelect,
      handleIndexCheckChange,
      handleSelectCheckChange,
      defaultRowSelection,
      handleColumnFixed,
      getPopupContainer,
    };
  },
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-column-setting';

.table-column-drag-icon {
  margin: 0 5px;
  cursor: move;
}
.tree-node {
  width: 100%;
  display: flex;
  justify-content: space-between;
}
.@{prefix-cls} {
  &__popover-title {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  &__check-item {
    display: flex;
    align-items: center;
    min-width: 100%;
    padding: 4px 16px 8px 0;

    .ant-checkbox-wrapper {
      width: 100%;

      &:hover {
        color: var(--ant-primary-color);
      }
    }
  }

  &__fixed-left,
  &__fixed-right {
    color: rgb(0 0 0 / 45%);
    cursor: pointer;

    &.active,
    &:hover {
      color: var(--ant-primary-color);
    }

    &.disabled {
      color: @disabled-color;
      cursor: not-allowed;
    }
  }

  &__fixed-right {
    transform: rotate(180deg);
  }

  &__cloumn-list {
    svg {
      width: 1em !important;
      height: 1em !important;
    }

    .ant-popover-inner-content {
      // max-height: 360px;
      padding-right: 0;
      padding-left: 0;
      // overflow: auto;
    }

    .ant-checkbox-group {
      width: 100%;
      min-width: 260px;
      // flex-wrap: wrap;
    }

    .scrollbar {
      height: 220px;
    }
  }
}
</style>
